const { AdView, User, sequelize } = require('../models')
const logger = require('../utils/logger')
const redisClient = require('../config/redis')
const { Op } = require('sequelize')

/**
 * 风险评估服务
 */
class RiskAssessmentService {
  /**
   * 评估用户风险等级
   * @param {number} userId - 用户ID
   * @param {string} ipAddress - IP地址
   * @param {string} userAgent - 用户代理
   * @returns {Object} 风险评估结果
   */
  static async assessUserRisk(userId, ipAddress, userAgent) {
    try {
      const assessments = [
        this.assessUserHistory(userId),
        this.assessIPReputation(ipAddress),
        this.assessDeviceBehavior(ipAddress, userAgent),
        this.assessTimePattern(userId),
        this.assessGeographicPattern(ipAddress)
      ]
      
      const results = await Promise.all(assessments)
      
      // 计算综合风险分数
      const riskScore = this.calculateRiskScore(results)
      const riskLevel = this.getRiskLevel(riskScore)
      
      return {
        riskScore,
        riskLevel,
        assessments: results,
        recommendations: this.getRecommendations(riskLevel, results)
      }
    } catch (error) {
      logger.error('风险评估失败:', error)
      return {
        riskScore: 50,
        riskLevel: 'medium',
        error: error.message
      }
    }
  }
  
  /**
   * 评估用户历史行为
   * @param {number} userId - 用户ID
   * @returns {Object} 评估结果
   */
  static async assessUserHistory(userId) {
    try {
      const user = await User.findByPk(userId)
      if (!user) {
        return {
          category: 'user_history',
          score: 100,
          reason: '用户不存在'
        }
      }
      
      // 检查用户注册时间
      const registrationTime = new Date(user.created_at)
      const now = new Date()
      const daysSinceRegistration = (now - registrationTime) / (1000 * 60 * 60 * 24)
      
      let score = 0
      
      // 新用户风险较高
      if (daysSinceRegistration < 1) {
        score += 30
      } else if (daysSinceRegistration < 7) {
        score += 15
      }
      
      // 检查用户观看历史
      const totalViews = await AdView.count({
        where: { user_id: userId }
      })
      
      const completedViews = await AdView.count({
        where: {
          user_id: userId,
          is_completed: 1
        }
      })
      
      // 计算完成率
      const completionRate = totalViews > 0 ? completedViews / totalViews : 0
      
      // 异常完成率增加风险
      if (completionRate > 0.95 || completionRate < 0.1) {
        score += 25
      }
      
      // 检查观看频率
      const recentViews = await AdView.count({
        where: {
          user_id: userId,
          created_at: {
            [Op.gte]: new Date(Date.now() - 24 * 60 * 60 * 1000)
          }
        }
      })
      
      if (recentViews > 40) {
        score += 20
      }
      
      return {
        category: 'user_history',
        score: Math.min(score, 100),
        details: {
          daysSinceRegistration,
          totalViews,
          completionRate,
          recentViews
        }
      }
    } catch (error) {
      logger.error('评估用户历史失败:', error)
      return {
        category: 'user_history',
        score: 50,
        error: error.message
      }
    }
  }
  
  /**
   * 评估IP声誉
   * @param {string} ipAddress - IP地址
   * @returns {Object} 评估结果
   */
  static async assessIPReputation(ipAddress) {
    try {
      let score = 0
      
      // 检查IP下的用户数量
      const userCount = await User.count({
        // 假设我们记录了用户注册时的IP
        // where: { register_ip: ipAddress }
      })
      
      if (userCount > 20) {
        score += 40
      } else if (userCount > 10) {
        score += 20
      }
      
      // 检查IP下的观看记录
      const ipViews = await AdView.count({
        where: {
          ip_address: ipAddress,
          created_at: {
            [Op.gte]: new Date(Date.now() - 24 * 60 * 60 * 1000)
          }
        }
      })
      
      if (ipViews > 80) {
        score += 30
      } else if (ipViews > 50) {
        score += 15
      }
      
      // 检查IP是否在监控列表中
      const monitorCount = await this.getIPMonitorCount(ipAddress)
      if (monitorCount > 5) {
        score += 25
      }
      
      return {
        category: 'ip_reputation',
        score: Math.min(score, 100),
        details: {
          userCount,
          ipViews,
          monitorCount
        }
      }
    } catch (error) {
      logger.error('评估IP声誉失败:', error)
      return {
        category: 'ip_reputation',
        score: 50,
        error: error.message
      }
    }
  }
  
  /**
   * 评估设备行为
   * @param {string} ipAddress - IP地址
   * @param {string} userAgent - 用户代理
   * @returns {Object} 评估结果
   */
  static async assessDeviceBehavior(ipAddress, userAgent) {
    try {
      let score = 0
      
      // 检查User-Agent是否异常
      if (!userAgent || userAgent.length < 50) {
        score += 30
      }
      
      // 检查是否包含常见的爬虫标识
      const botPatterns = [
        /bot/i, /crawler/i, /spider/i, /scraper/i,
        /curl/i, /wget/i, /python/i, /java/i
      ]
      
      for (const pattern of botPatterns) {
        if (pattern.test(userAgent)) {
          score += 50
          break
        }
      }
      
      // 检查设备指纹的使用频率
      const deviceViews = await AdView.count({
        where: {
          ip_address: ipAddress,
          user_agent: userAgent,
          created_at: {
            [Op.gte]: new Date(Date.now() - 60 * 60 * 1000) // 1小时内
          }
        }
      })
      
      if (deviceViews > 10) {
        score += 35
      } else if (deviceViews > 5) {
        score += 20
      }
      
      return {
        category: 'device_behavior',
        score: Math.min(score, 100),
        details: {
          userAgentLength: userAgent?.length || 0,
          deviceViews,
          suspiciousUserAgent: botPatterns.some(pattern => pattern.test(userAgent))
        }
      }
    } catch (error) {
      logger.error('评估设备行为失败:', error)
      return {
        category: 'device_behavior',
        score: 50,
        error: error.message
      }
    }
  }
  
  /**
   * 评估时间模式
   * @param {number} userId - 用户ID
   * @returns {Object} 评估结果
   */
  static async assessTimePattern(userId) {
    try {
      let score = 0
      
      // 获取用户最近的观看记录
      const recentViews = await AdView.findAll({
        where: {
          user_id: userId,
          created_at: {
            [Op.gte]: new Date(Date.now() - 24 * 60 * 60 * 1000)
          }
        },
        order: [['created_at', 'ASC']]
      })
      
      if (recentViews.length < 3) {
        return {
          category: 'time_pattern',
          score: 0,
          details: { viewCount: recentViews.length }
        }
      }
      
      // 分析时间间隔
      const intervals = []
      for (let i = 1; i < recentViews.length; i++) {
        const interval = new Date(recentViews[i].created_at) - new Date(recentViews[i-1].created_at)
        intervals.push(interval)
      }
      
      // 计算间隔的标准差
      const avgInterval = intervals.reduce((a, b) => a + b, 0) / intervals.length
      const variance = intervals.reduce((sum, interval) => sum + Math.pow(interval - avgInterval, 2), 0) / intervals.length
      const stdDev = Math.sqrt(variance)
      
      // 间隔过于规律可能是机器行为
      if (stdDev < 5000 && avgInterval < 120000) { // 标准差小于5秒且平均间隔小于2分钟
        score += 40
      }
      
      // 检查是否在非正常时间大量观看（如凌晨2-6点）
      const nightViews = recentViews.filter(view => {
        const hour = new Date(view.created_at).getHours()
        return hour >= 2 && hour <= 6
      }).length
      
      if (nightViews > recentViews.length * 0.5) {
        score += 25
      }
      
      return {
        category: 'time_pattern',
        score: Math.min(score, 100),
        details: {
          viewCount: recentViews.length,
          avgInterval: Math.round(avgInterval / 1000), // 转换为秒
          stdDev: Math.round(stdDev / 1000),
          nightViews
        }
      }
    } catch (error) {
      logger.error('评估时间模式失败:', error)
      return {
        category: 'time_pattern',
        score: 50,
        error: error.message
      }
    }
  }
  
  /**
   * 评估地理模式
   * @param {string} ipAddress - IP地址
   * @returns {Object} 评估结果
   */
  static async assessGeographicPattern(ipAddress) {
    try {
      let score = 0
      
      // 这里可以集成IP地理位置服务
      // 检查IP是否来自高风险地区或使用代理
      
      // 模拟地理位置检查
      const geoInfo = {
        country: 'CN',
        isProxy: false,
        isVPN: false,
        riskLevel: 'low'
      }
      
      if (geoInfo.isProxy || geoInfo.isVPN) {
        score += 60
      }
      
      if (geoInfo.riskLevel === 'high') {
        score += 40
      } else if (geoInfo.riskLevel === 'medium') {
        score += 20
      }
      
      return {
        category: 'geographic_pattern',
        score: Math.min(score, 100),
        details: geoInfo
      }
    } catch (error) {
      logger.error('评估地理模式失败:', error)
      return {
        category: 'geographic_pattern',
        score: 50,
        error: error.message
      }
    }
  }
  
  /**
   * 计算综合风险分数
   * @param {Array} assessments - 评估结果数组
   * @returns {number} 风险分数
   */
  static calculateRiskScore(assessments) {
    const weights = {
      user_history: 0.25,
      ip_reputation: 0.25,
      device_behavior: 0.25,
      time_pattern: 0.15,
      geographic_pattern: 0.10
    }
    
    let totalScore = 0
    let totalWeight = 0
    
    for (const assessment of assessments) {
      const weight = weights[assessment.category] || 0.1
      totalScore += assessment.score * weight
      totalWeight += weight
    }
    
    return totalWeight > 0 ? Math.round(totalScore / totalWeight) : 50
  }
  
  /**
   * 获取风险等级
   * @param {number} riskScore - 风险分数
   * @returns {string} 风险等级
   */
  static getRiskLevel(riskScore) {
    if (riskScore >= 80) {
      return 'high'
    } else if (riskScore >= 50) {
      return 'medium'
    } else if (riskScore >= 20) {
      return 'low'
    } else {
      return 'very_low'
    }
  }
  
  /**
   * 获取建议措施
   * @param {string} riskLevel - 风险等级
   * @param {Array} assessments - 评估结果
   * @returns {Array} 建议措施
   */
  static getRecommendations(riskLevel, assessments) {
    const recommendations = []
    
    switch (riskLevel) {
      case 'high':
        recommendations.push('立即限制访问')
        recommendations.push('加入黑名单')
        recommendations.push('人工审核')
        break
      case 'medium':
        recommendations.push('增加验证步骤')
        recommendations.push('限制观看频率')
        recommendations.push('加强监控')
        break
      case 'low':
        recommendations.push('正常处理')
        recommendations.push('定期监控')
        break
      default:
        recommendations.push('正常处理')
    }
    
    // 根据具体评估结果添加针对性建议
    for (const assessment of assessments) {
      if (assessment.score > 70) {
        switch (assessment.category) {
          case 'user_history':
            recommendations.push('验证用户身份')
            break
          case 'ip_reputation':
            recommendations.push('限制IP访问')
            break
          case 'device_behavior':
            recommendations.push('验证设备真实性')
            break
          case 'time_pattern':
            recommendations.push('分析行为模式')
            break
          case 'geographic_pattern':
            recommendations.push('验证地理位置')
            break
        }
      }
    }
    
    return [...new Set(recommendations)] // 去重
  }
  
  /**
   * 获取IP监控次数
   * @param {string} ipAddress - IP地址
   * @returns {number} 监控次数
   */
  static async getIPMonitorCount(ipAddress) {
    try {
      const key = `monitor:ip:${ipAddress}`
      const count = await redisClient.get(key)
      return parseInt(count) || 0
    } catch (error) {
      logger.error('获取IP监控次数失败:', error)
      return 0
    }
  }
  
  /**
   * 记录风险评估结果
   * @param {number} userId - 用户ID
   * @param {string} ipAddress - IP地址
   * @param {Object} riskAssessment - 风险评估结果
   */
  static async recordRiskAssessment(userId, ipAddress, riskAssessment) {
    try {
      logger.info('风险评估结果', {
        userId,
        ipAddress,
        riskScore: riskAssessment.riskScore,
        riskLevel: riskAssessment.riskLevel,
        recommendations: riskAssessment.recommendations,
        timestamp: new Date().toISOString()
      })
      
      // 可以将风险评估结果存储到数据库或缓存中
      const key = `risk:assessment:${userId}:${Date.now()}`
      await redisClient.setex(key, 3600, JSON.stringify(riskAssessment)) // 1小时过期
      
    } catch (error) {
      logger.error('记录风险评估结果失败:', error)
    }
  }
}

module.exports = RiskAssessmentService